"""
本文件定义PyStationCapacity程序的数据结构，并实现csv格式文件数据读写功能 
"""

import csv
import datetime

from xml.etree.ElementTree import Element
from xml.etree.ElementTree import SubElement
from xml.etree.ElementTree import ElementTree

import os


class PSCData:
    ''' 数据 '''

    # 输入文件夹及文件名（默认值如下）
    InputFileDirectory = "./LocalData"
    StationFileName = "Station.csv"
    NodeFileName = "Node.csv"
    LinkFileName = "Link.csv"
    SegmentFileName = "Segment.csv"
    CellFileName = "Cell.csv"
    RouteFileName = "Route.csv"
    StationRouteTimeFileName = "StationRouteTime.csv"
    TrainFileName = "Train.csv"
    TimetableFileName="Timetable.csv"

    # 输出文件夹及文件名（默认值如下）
    OutputFileDirectory = "./Output"
    OutputTimetableFileName = "OutputTimetable.csv"
    OutputCellOccupancyFileName = "CellOccupancy.csv"

    # 参数
    LeftShiftTolerance = 50400
    ''' 作业时刻最大向左偏移量 '''

    RightShiftTolerance = 50400
    ''' 作业时刻最大向右偏移量 '''

    # 集合
    StationList = list()
    ''' 车站集合 '''

    SegmentList = list()
    ''' 区间集合 '''

    CurrentStation = None
    ''' 当前车站 '''

    TrainList = list()
    ''' 列车集合 '''

    def LoadData(self):
        ''' 读取数据 '''
        self.StationList.clear()
        self.SegmentList.clear()
        self.CurrentStation = None
        self.TrainList.clear()

        # 读取车站数据
        f = open(self.InputFileDirectory + "/" + self.StationFileName, "r", encoding='utf-8')
        for item in csv.DictReader(f):
            sta = Station()
            sta.ID = item["ID"]
            sta.Name = item["Name"]
            self.StationList.append(sta)
        self.CurrentStation = self.StationList[0]

        # 读取车站节点数据
        f = open(self.InputFileDirectory + "/" + self.NodeFileName, "r")
        self.CurrentStation.NodeList = list()
        for item in csv.DictReader(f):
            n = Node()
            n.ID = item["ID"]
            n.X = float(item["X"])
            n.Y = float(item["Y"])
            self.CurrentStation.NodeList.append(n)

        # 读取车站边数据
        f = open(self.InputFileDirectory + "/" + self.LinkFileName, "r")
        self.CurrentStation.LinkList = list()
        for item in csv.DictReader(f):
            l = Link()
            l.ID = item["ID"]
            l.FromNode = [n for n in self.CurrentStation.NodeList if n.ID == item["FromNodeID"]][0]
            l.ToNode = [n for n in self.CurrentStation.NodeList if n.ID == item["ToNodeID"]][0]
            self.CurrentStation.LinkList.append(l)
        
        # 读取区间数据
        f = open(self.InputFileDirectory + "/" + self.SegmentFileName,"r", encoding='utf-8')
        for item in csv.DictReader(f):
            seg = Segment()
            seg.ID = item["ID"]
            seg.FromStation = [sta for sta in self.StationList if sta.ID == item["FromStationID"]][0]
            seg.ToStation = [sta for sta in self.StationList if sta.ID == item["ToStationID"]][0]
            seg.Name = item["Name"]
            self.SegmentList.append(seg)

        # 读取轨道电路数据
        self.CurrentStation.CellList = list()
        f = open(self.InputFileDirectory+"/"+self.CellFileName, "r")
        for item in csv.DictReader(f):
            c = Cell()
            c.ID = item["ID"]
            c.TrackIDListStr = item["TrackIDList"].replace(';',',')
            self.CurrentStation.CellList.append(c)
        f.close()

        # 读取作业进路数据
        self.CurrentStation.RouteList = list()
        f = open(self.InputFileDirectory+"/"+self.RouteFileName, "r")
        for item in csv.DictReader(f):
            r = Route()
            r.ID = item["ID"]
            r.TrackIDListStr = item["TrackIDList"].replace(';',',')
            r.CellList = list()
            for cid in item["CellIDList"].split(';'):
                c = [x for x in self.CurrentStation.CellList if x.ID == cid][0]
                r.CellList.append(c)

            r.TagList = list()
            for tag in item["Tag"].split(';'):
                r.TagList.append(tag)

            self.CurrentStation.RouteList.append(r)
        f.close()

        # 读取轨道电路占用数据
        self.CurrentStation.CellOccupationTimeSetList = list()
        f = open(self.InputFileDirectory+"/"+self.StationRouteTimeFileName, "r")
        cotSet = CellOccupationTimeSet()
        cotSet.ID = "0"
        cotSet.CellOccupationTimeDict = dict()

        self.CurrentStation.CellOccupationTimeSetList.append(cotSet)
        for item in csv.DictReader(f):
            cot = CellOccupationTime()
            cot.Route = [
                route for route in self.CurrentStation.RouteList if route.ID == item["RouteID"]][0]
            cot.Cell = [
                cell for cell in self.CurrentStation.CellList if cell.ID == item["CellID"]][0]
            cot.StartOccupationShift = int(item["StartOccupationShift"])
            cot.EndOccupationShift = int(item["EndOccupationShift"])
            cotSet.CellOccupationTimeDict[cot.Route,cot.Cell] = cot
        f.close()

        # 读取列车数据
        f = open(self.InputFileDirectory+"/"+self.TrainFileName, "r")
        for item in csv.DictReader(f):
            tr = Train()
            tr.ID = item["ID"]
            tr.Type = item["Type"]
            tr.TrainMovementList = list()
            self.TrainList.append(tr)
        f.close()

        # 读取列车时刻表数据
        f = open(self.InputFileDirectory+"/"+self.TimetableFileName, "r")
        for item in csv.DictReader(f):
            tr = [tr for tr in self.TrainList if tr.ID == item["TrainID"]][0]
            m = TrainMovement()
            m.ID = item["ID"]
            m.Train = tr
            m.Type = item["MovementType"]
            m.Seq = item["Sequence"]
            m.OccupancyTimeDict = dict()

            m.RouteTagList = list()
            for tag in item["RouteTag"].split(';'):
                m.RouteTagList.append(tag)

            m.OrgStartTime = datetime.datetime.strptime(
                item["StartTime"], "%H:%M:%S")
            m.OrgEndTime = datetime.datetime.strptime(
                item["EndTime"], "%H:%M:%S")

            m.MinDuration = int(item["MinDuration"])
            m.MaxDuration = int(item["MaxDuration"])

            tr.TrainMovementList.append(m)

        f.close()

    def WriteSolution(self):
        ''' 输出求解结果 '''
        f = open(self.OutputFileDirectory+"/"+self.OutputTimetableFileName, "w")
        f.writelines("TrainID,MovementType,RouteID,StartTime,EndTime\n")
        for tr in self.TrainList:
            for m in tr.TrainMovementList:
                f.writelines("{0},{1},{2},{3},{4}\n".format(
                    tr.ID, m.Type, m.SelectingRoute.ID, m.StartTime.strftime("%H:%M:%S"), m.EndTime.strftime("%H:%M:%S")))
        f.close()

        f = open(self.OutputFileDirectory+"/"+self.OutputCellOccupancyFileName, "w")
        f.writelines("TrainID,MovementID,MovementType,CellID,OccupancyStartTime,OccupancyEndTime\n")
        for tr in self.TrainList:
            for m in tr.TrainMovementList:
                for c in m.OccupancyTimeDict:
                    if c not in m.SelectingRoute.CellList:
                        continue
                    f.writelines("{0},{1},{2},{3},{4},{5}\n".format(
                        tr.ID, m.ID, m.Type, c.ID,
                        self.ConvertTotalSecToDateTime(m.OccupancyTimeDict[c][0]).strftime("%H:%M:%S"),
                        self.ConvertTotalSecToDateTime(m.OccupancyTimeDict[c][1]).strftime("%H:%M:%S")))
        f.close()


    def WriteMetaData(self, fileName):
        ''' 输出绘图数据MetaData.xml文件 '''
        root = Element("TimetableMetaData")
        root.attrib["LineName"] = "高铁"

        stationSetEle = SubElement(root, "StationSet")
        for station in self.StationList:
            stationEle = SubElement(stationSetEle, "Station")
            stationEle.attrib["ID"] = station.ID
            stationEle.attrib["Name"] = station.Name
            stationEle.attrib["Miles"] = str(station.Miles)

            stationTrackSetEle = SubElement(stationEle, "StationTracks")
            
            if station.LinkList != None:
                stationTrackSectionSetEle = SubElement(stationEle, "TrackSections")
                for link in station.LinkList:
                    ts = SubElement(stationTrackSectionSetEle, "TS")
                    ts.attrib["ID"] = link.ID
                    ts.attrib["StartX"] = str(link.FromNode.X)
                    ts.attrib["StartY"] = str(link.FromNode.Y)
                    ts.attrib["EndX"] = str(link.ToNode.X)
                    ts.attrib["EndY"] = str(link.ToNode.Y)
            
            if station.CellList != None:
                cellSetEle = SubElement(stationEle, "Cells")
                for cell in station.CellList:
                    c = SubElement(cellSetEle, "Cell")
                    c.attrib["ID"] = cell.ID
                    c.attrib["StationTrackSections"] = cell.TrackIDListStr
                    c.attrib["Type"] = "S"
            
            if station.RouteList != None:
                routeSetEle = SubElement(stationEle, "Routes")
                for r in station.RouteList:
                    route = SubElement(routeSetEle, "Route")
                    route.attrib["ID"] = r.ID
                    route.attrib["StationTrackSections"] = r.TrackIDListStr
                    route.attrib["Cells"] = r.GetCellStr()
                    route.attrib["Description"] = ""

        segmentSetEle = SubElement(root, "SegmentSet")
        segmentListStr = ""
        for segment in self.SegmentList:
            segmentEle = SubElement(segmentSetEle, "Segment")
            segmentEle.attrib["ID"] = segment.ID
            segmentEle.attrib["FromStationID"] = segment.FromStation.ID
            segmentEle.attrib["ToStationID"] = segment.ToStation.ID
            segmentEle.attrib["Name"] = segment.ID
            segmentListStr += segment.ID + ","

        timetableBlockSetEle = SubElement(root, "TimetableBlockSet")
        timetableBlockEle = SubElement(timetableBlockSetEle, "TimetableBlock")
        timetableBlockEle.attrib["ID"] = "1"
        timetableBlockEle.attrib["SegmentList"] = segmentListStr.strip(',')
        for station in self.StationList:
            stationEle = SubElement(timetableBlockEle, "Station")
            stationEle.attrib["ID"] = station.ID
            stationEle.attrib["Position"] = str(station.Miles)
            stationEle.attrib["TrackVisibility"] = "False"

        timetableSetEle = SubElement(root, "TimetableSet")
        timetableEle = SubElement(timetableSetEle, "Timetable")
        timetableEle.attrib["ID"] = "0"
        timetableEle.attrib["Name"] = "运行图"
        timetableEle.attrib["TimetableRef"] = "0"

        SubElement(timetableEle, "BlockadeSet")
        SubElement(timetableEle, "RollingStockPathSet")

        trainSetEle = SubElement(timetableEle, "TrainSet")
        for train in self.TrainList:
            trainEle = SubElement(trainSetEle, "Train")
            trainEle.attrib["ID"] = train.ID
            trainEle.attrib["Name"] = train.ID
            trainEle.attrib["Type"] = "Passenger"
            trainEle.attrib["OriginType"] = "Departure"
            trainEle.attrib["SinkType"] = "Termination"
            trainEle.attrib["RefRunDir"] = "0"
            trainEle.attrib["RefLineName"] = ""
            trainEle.attrib["RefPreTrainID"] = ""
            trainEle.attrib["RefNextTrainID"] = ""
            trainEle.attrib["RefNextTrainName"] = ""

            trainEle.attrib["Route"] = "XA-ZDD,E1-ZDD,E2-ZDD,XAP-ZDD,ZDD-SJZ,ZDD-XA,ZDD-E1,ZDD-E2,ZDD-XAP,SJZ-ZDD"

            eventListEle = SubElement(trainEle, "Timetable")
            
            arrEventEle = SubElement(eventListEle, "Event")
            arrEventEle.attrib["StationID"] = self.CurrentStation.ID
            arrEventEle.attrib["Type"] = "Arrival"
            arrEventEle.attrib["Track"] = ""
            arrEventEle.attrib["Sequence"] = "1"
            arrEventEle.attrib["SegmentID"] = ""

            depEventEle = SubElement(eventListEle, "Event")
            depEventEle.attrib["StationID"] = self.CurrentStation.ID
            depEventEle.attrib["Type"] = "Departure"
            depEventEle.attrib["Track"] = ""
            depEventEle.attrib["Sequence"] = "2"
            depEventEle.attrib["SegmentID"] = ""

            smSetEle = SubElement(trainEle, "StationMovements")
            for m in train.TrainMovementList:
                smEle = SubElement(smSetEle, "SM")
                smEle.attrib["StationID"] = self.CurrentStation.ID
                smEle.attrib["RouteID"] = m.SelectingRoute.ID
                smEle.attrib["Description"] = ""
                smEle.attrib["Type"] = m.Type

                if m.Type == "Arr":
                    arrEventEle.attrib["Time"] = m.StartTime.strftime("%H:%M:%S")
                elif m.Type == "Dep":
                    depEventEle.attrib["Time"] = m.StartTime.strftime("%H:%M:%S")

                for c in m.OccupancyTimeDict:
                    if c not in m.SelectingRoute.CellList:
                        continue
                    ceocEle = SubElement(smEle, "CeOc")
                    ceocEle.attrib["CellID"] = c.ID
                    ceocEle.attrib["Start"] = self.ConvertTotalSecToDateTime(m.OccupancyTimeDict[c][0]).strftime("%H:%M:%S")
                    ceocEle.attrib["End"] = self.ConvertTotalSecToDateTime(m.OccupancyTimeDict[c][1]).strftime("%H:%M:%S")

        tree = ElementTree(root)
        tree.write(fileName, encoding="utf-8")


    def ConvertDateTimeToTotalSec(self, dateTime):
        ''' 将DateTime转换为总秒数 '''
        HH = dateTime.hour
        mm = dateTime.minute
        ss = dateTime.second
        return ss + mm * 60 + HH * 3600
    

    def ConvertTotalSecToDateTime(self, totalSec):
        ''' 将总秒数转换为DateTime '''
        HH = int(totalSec/3600)
        mm = int((totalSec-HH*3600)/60)
        ss = int(totalSec - HH * 3600 - mm * 60)
        if HH == 24:
            HH = 0
        return datetime.datetime(1900, 1, 1, HH, mm, ss)
    

    def IsRouteWithConn(self, route1, route2):
        ''' 根据进路的首末轨道电路判断进路是否连通 '''
        if route1.CellList[route1.CellList.__len__()-1] == route2.CellList[0]:
            return True
        else:
            return False
        
    
    def GenerateCandidateRoute(self):
        ''' 生成作业可选进路 '''
        for tr in self.TrainList:
            for m in tr.TrainMovementList:
                m.CandidateRouteList = list()
                for r in self.CurrentStation.RouteList:
                    isMatching = True
                    for tag in m.RouteTagList:
                        if tag not in r.TagList:
                            isMatching = False
                    if isMatching:
                        m.CandidateRouteList.append(r)
                if m.CandidateRouteList.__len__() == 0:
                    print("作业{0}无可行进路！".format(m.ID))


    def GenerateCandidateCell(self):
        ''' 生成作业可选轨道电路 '''
        for tr in self.TrainList:
            for m in tr.TrainMovementList:
                m.CandidateCellList = list()
                for r in m.CandidateRouteList:
                    for c in r.CellList:
                        m.CandidateCellList.append(c)

class Station:
    ''' 车站 '''
    
    ID = ""
    ''' 车站ID '''

    Name = ""
    ''' 车站名 '''

    Miles = 0.0
    ''' 中心里程 '''

    LinkList = None
    ''' 轨道段集合 '''

    CellList = None
    ''' 轨道电路集合 '''

    RouteList = None
    ''' 进路集合 '''

    CellOccupationTimeSetList = None
    ''' 轨道电路占用时间标准集合的列表 '''


class Node:
    ID = ""
    X = 0.0
    Y = 0.0


class Link:
    ID = ""
    FromNode = None
    ToNode = None


class Segment:
    '''区间类'''

    ID = ""
    ''' 区间ID '''

    Name = ""
    ''' 区间名 '''

    FromStation = None
    ''' 起始车站 '''

    ToStation = None
    ''' 中止车站 '''


class Cell:
    ''' 轨道电路 '''

    ID = ""
    ''' 轨道电路ID '''

    TrackIDListStr = ""
    ''' 轨道电路所包含的线路段ID '''


class Route:
    ''' 径路 '''

    ID = ""
    ''' 径路ID '''

    CellList = None
    ''' 轨道电路列表 '''

    TagList = None
    ''' 标签列表 '''

    TrackIDListStr = ""
    ''' 进路所包含的线路段ID '''

    def GetCellStr(self):
        ''' 获取轨道电路表示字符串 '''
        cellStr = ""
        for c in self.CellList:
            cellStr+=c.ID + ","
        return cellStr[:-1]


class CellOccupationTimeSet:
    ''' 轨道电路占用时间标准集合 '''

    ID = ""
    ''' 轨道电路占用时间标准ID '''

    CellOccupationTimeDict = None
    ''' 轨道电路占用时间标准列表 '''


class CellOccupationTime:
    ''' 轨道电路占用时间标准 '''

    Route = None
    ''' 径路 '''

    Cell = None
    ''' 轨道电路 '''

    StartOccupationShift = -1
    ''' 开始占用时间偏移（相对于进路的StartTime） '''

    EndOccupationShift = -1
    ''' 结束时间偏移（相对于进路的EndTime） '''


class Train:
    ''' 列车 '''

    ID = ""
    ''' 列车ID '''

    Type = ""
    ''' 列车种类 '''

    TrainMovementList = None
    ''' 列车作业列表 '''


class TrainMovement:
    ''' 列车作业 '''
    ID = ""
    ''' 列车作业ID '''

    Train = None
    ''' 列车 '''

    Type = ""
    ''' 作业种类 '''

    Seq = -1
    ''' 顺序编号 '''

    RouteTagList = None
    ''' 进路筛选标签（进路的标签必须覆盖此列表中的标签才能被选） '''

    OrgStartTime = datetime.datetime.strptime("00:00:00", "%H:%M:%S")
    ''' 原计划开始时间 '''

    OrgEndTime = datetime.datetime.strptime("00:00:00", "%H:%M:%S")
    ''' 原计划结束时间 '''

    StartTime = datetime.datetime.strptime("00:00:00", "%H:%M:%S")
    ''' 开始时间 '''

    EndTime = datetime.datetime.strptime("00:00:00", "%H:%M:%S")
    ''' 结束时间 '''

    SelectingRoute = None
    ''' 选用进路 '''

    MinDuration = -1
    ''' 最小持续时间 '''

    MaxDuration = -1
    ''' 最大持续时间 '''
    
    OccupancyTimeDict = None
    ''' 作业占用轨道电路时间 '''

    CandidateRouteList = None
    ''' 可能使用的进路列表 '''

    CandidateCellList = None
    ''' 可能使用的轨道电路列表 '''
